﻿using System;
using System.IO;
using System.Reflection;
using System.Threading.Tasks;
using IMDBApi.Contracts;
using log4net;
using log4net.Config;
using System.Collections.Concurrent;
using System.Collections.Generic;

// Configure log4net using the .config file

[assembly: XmlConfigurator(Watch = true)]

namespace IMDBApi.Implementation
{
    /// <summary>
    ///     ...
    /// </summary>
    public class PollingService
    {
        /// <summary>
        ///     Create a logger for use in this class
        /// </summary>
        private static readonly ILog log = LogManager.GetLogger(typeof(PollingService));

        /// <summary>
        ///     ...
        /// </summary>
        private readonly FileSystemWatcher watcher = new FileSystemWatcher
        {
            IncludeSubdirectories = true
        };

        /// <summary>
        ///     Constructor method.
        /// </summary>
        public PollingService(string path)
        {
            watcher.Created += FileSystemEvent;

            if (path == string.Empty || !Directory.Exists(path))
            {
                log.Error(MethodBase.GetCurrentMethod().Name + ": A valid path is required.");
                return;
            }

            watcher.Path = path;
        }

        /// <summary>
        ///     ...
        /// </summary>
        public bool IsStarted { get; private set; }

        /// <summary>
        ///     Destructor method.
        /// </summary>
        ~PollingService()
        {
            if (IsStarted)
                Stop();
        }

        /// <summary>
        ///     ...
        /// </summary>
        public void Start()
        {
            log.Info(MethodBase.GetCurrentMethod().Name);

            watcher.EnableRaisingEvents = true;
            IsStarted = true;
        }

        /// <summary>
        ///     ...
        /// </summary>
        public void Stop()
        {
            log.Info(MethodBase.GetCurrentMethod().Name);

            watcher.EnableRaisingEvents = false;
            IsStarted = false;
        }

        /// <summary>
        ///     ...
        /// </summary>
        /// <param name="command"></param>
        public void OnCustomCommand(int command)
        {
            log.Info(MethodBase.GetCurrentMethod().Name);
        }

        /// <summary>
        ///     ...
        /// </summary>
        public void CheckAllFiles()
        {
            // Nur alle Dateien prüfen die kein "Rating" im Dateinamen enthalten.
            // Diese wurden erhielten bereits eine Bewertung.
            List<FileInfo> files = PollingUtils.GetUnratedFiles(watcher.Path);

            // Taskliste erstellen.
            Task[] tasks = new Task[files.Count];

            foreach (FileInfo file in files)
            {
                // Jede Datei einzelnd prozessieren.
                tasks[files.IndexOf(file)] = Task.Run(() => {
                    CheckFile(file);
                });
            }

            // Alle Bewertungen abwarten.
            Task.WaitAll(tasks);
        }

        /// <summary>
        ///     Checks a file for rating.
        /// </summary>
        /// <param name="fullFileName">filename</param>
        private void CheckFile(FileInfo file)
        {
            if (!file.Exists)
            {
                log.Error(MethodBase.GetCurrentMethod().Name + ": A valid path is required.");
                return;
            }

            if (file.Name.StartsWith("Rating"))
            {
                log.Info(MethodBase.GetCurrentMethod().Name + ": This file has already rated.");
                return;
            }

            log.Info(MethodBase.GetCurrentMethod().Name + ": Started Task for " + file.FullName);

            // Query ImdbApi
            IMovie movie = QueryMovie(Path.GetFileNameWithoutExtension(file.Name));

            try
            {
                string newFileName = Path.Combine(file.DirectoryName, "Rating" + movie.Rating + "#" + file.Name);

                log.Info(MethodBase.GetCurrentMethod().Name + ": File.Move(...) " + newFileName);

                if (!File.Exists(newFileName))
                {
                    file.MoveTo(newFileName);
                }
            }
            catch (FileNotFoundException ex)
            {
                log.Error(MethodBase.GetCurrentMethod().Name + ": " + ex.Message + ".");
                throw;
            }
        }

        /// <summary>
        ///     ...
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void FileSystemEvent(object sender, FileSystemEventArgs e)
        {
            CheckFile(new FileInfo(e.FullPath));
        }

        /// <summary>
        ///     Querys the IMDB Webservice for given movie.
        /// </summary>
        /// <param name="searchTerms">Search terms.</param>
        /// <returns>Returns an IMovie object.</returns>
        private IMovie QueryMovie(string searchTerms)
        {
            return new Query().FindMovie(searchTerms);
        }
    }
}